<!--
Copyright (C) 2016-2023 Jones Magloire @Joxit

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<tag-history>
  <material-card>
    <div class="material-card-title-action">
      <material-button
        text-color="var(--neutral-text)"
        color="inherit"
        waves-color="var(--hover-background)"
        waves-center="true"
        rounded="true"
        href="{ toTaglist() }"
        icon
      >
        <i class="material-icons">arrow_back</i>
      </material-button>
      <h2>History of { props.image }:{ props.tag } <i class="material-icons">history</i></h2>
      <material-button
        text-color="var(--accent-text)"
        color="inherit"
        waves-color="var(--hover-background)"
        waves-center="true"
        rounded="true"
        outlined
        onClick="{ showDockerfile }"
      >
        Dockerfile
      </material-button>
    </div>
  </material-card>
  <div if="{ !state.loadend }" class="spinner-wrapper">
    <material-spinner></material-spinner>
  </div>
  <dockerfile
    opened="{ state.showDockerfile }"
    on-close="{ onDockerfileClose }"
    elements="{ state.elements }"
  ></dockerfile>

  <material-tabs
    if="{ state.archs && state.loadend }"
    color="var(--background)"
    text-color="var(--primary-text)"
    text-selected-color="var(--accent-text)"
    line-color="var(--background)"
    line-selected-color="var(--accent-text)"
    useLine="{ true }"
    tabs="{ state.archs }"
    onTabChanged="{ onTabChanged }"
  ></material-tabs>

  <material-card each="{ element in state.elements }">
    <tag-history-element
      each="{ entry in element }"
      if="{ entry.value && entry.value.length > 0}"
      entry="{ entry }"
    ></tag-history-element>
  </material-card>
  <script>
    import { DockerImage } from '../../scripts/docker-image';
    import { bytesToSize } from '../../scripts/utils';
    import Dockerfile from '../dialogs/dockerfile.riot';
    import router from '../../scripts/router';
    import TagHistoryElement from './tag-history-element.riot';
    export default {
      components: {
        TagHistoryElement,
        Dockerfile,
      },
      onBeforeMount(props, state) {
        state.elements = [];
        state.image = new DockerImage(props.image, props.tag, {
          list: true,
          registryUrl: props.registryUrl,
          onNotify: props.onNotify,
          onAuthentication: props.onAuthentication,
          useControlCacheHeader: props.useControlCacheHeader,
          isRegistrySecured: props.isRegistrySecured,
        });
        state.image.fillInfo();
      },
      onMounted(props, state) {
        state.image.on('blobs', this.processBlobs);
        state.image.on('list', this.multiArchList);
      },
      onTabChanged(arch, idx) {
        const state = this.state;
        const { registryUrl, onNotify, useControlCacheHeader } = this.props;
        state.elements = [];
        state.image.variants[idx] =
          state.image.variants[idx] ||
          new DockerImage(this.props.image, arch.digest, {
            list: false,
            registryUrl,
            onNotify,
            useControlCacheHeader,
          });
        if (state.image.variants[idx].blobs) {
          return this.processBlobs(state.image.variants[idx].blobs);
        }
        state.image.variants[idx].fillInfo();
        state.image.variants[idx].on('blobs', this.processBlobs);
      },
      processBlobs(blobs) {
        const state = this.state;
        const { historyCustomLabels } = this.props;

        function exec(elt) {
          const guiElements = [];
          for (var attribute in elt) {
            if (elt.hasOwnProperty(attribute) && attribute != 'empty_layer') {
              const value = elt[attribute];
              const guiElement = modifySpecificAttributeTypes(attribute, value);
              guiElements.push(guiElement);
            }
          }
          return guiElements.sort(eltSort);
        }
        const elements = new Array(blobs.history.length + 1);
        elements[0] = exec(getConfig(blobs, { historyCustomLabels }));
        blobs.history.forEach(function (elt, i) {
          elements[blobs.history.length - i] = exec(elt);
        });
        this.update({
          elements,
          loadend: true,
        });
      },
      multiArchList(manifests) {
        manifests = manifests.manifests || manifests;
        const archs = manifests.map(function (manifest) {
          return {
            title:
              manifest.platform.os +
              '/' +
              manifest.platform.architecture +
              (manifest.platform.variant ? manifest.platform.variant : ''),
            digest: manifest.digest,
          };
        });
        this.update({
          archs,
        });
      },
      toTaglist() {
        return router.taglist(this.props.image);
      },
      showDockerfile() {
        this.update({ showDockerfile: true });
      },
      onDockerfileClose() {
        this.update({ showDockerfile: false });
      },
    };
    const eltIdx = function (e) {
      switch (e) {
        case 'created':
          return 1;
        case 'created_by':
          return 2;
        case 'size':
          return 3;
        case 'os':
          return 4;
        case 'architecture':
          return 5;
        case 'id':
          return 6;
        case 'linux':
          return 7;
        case 'docker_version':
          return 8;
        default:
          return 10;
      }
    };

    const eltSort = function (e1, e2) {
      return eltIdx(e1.key) - eltIdx(e2.key);
    };

    const parseCreatedBy = (value) => {
      if (value.startsWith('COPY')) {
        return {
          value: 'COPY',
          content: value.replace(/^COPY /, ''),
        };
      }
      let cmd = value.match(/\/bin\/sh *-c *#\(nop\) *([A-Z]+) (.*)/);
      return {
        value: (cmd && cmd[1]) || 'RUN',
        content: (cmd && cmd[2]) || value.replace(/^\/bin\/sh *-c *(#\(nop\))?/, ''),
      };
    };

    const modifySpecificAttributeTypes = function (key, value) {
      switch (key) {
        case 'created':
          return { key, value: new Date(value).toLocaleString() };
        case 'created_by':
          const cmd = value.match(/\/bin\/sh *-c *#\(nop\) *([A-Z]+) (.*)/);
          return {
            key,
            ...parseCreatedBy(value),
          };
        case 'size':
          return { key, value: bytesToSize(value) };
        case 'Entrypoint':
        case 'Cmd':
          return { key, value: (value || []).join(' ') };
        case 'Labels':
          return {
            key,
            value: Object.keys(value || {}).map(function (elt) {
              return value[elt] ? elt + '=' + value[elt] : '';
            }),
          };
        case 'Volumes':
        case 'ExposedPorts':
          return { key, value: Object.keys(value) };
      }
      return { key, value: value || '' };
    };

    const getConfig = function (blobs, { historyCustomLabels }) {
      const res = [
        'architecture',
        'User',
        'created',
        'docker_version',
        'os',
        'Cmd',
        'Entrypoint',
        'Env',
        'Labels',
        'User',
        'Volumes',
        'WorkingDir',
        'author',
        'id',
        'ExposedPorts',
        'name',
        'appVersion',
        'kubeVersion',
        'keywords',
        'home',
        'sources'
      ].reduce(function (acc, e) {
        const value = blobs[e] || (blobs.config && blobs.config[e]);
        if (value && e === 'architecture' && blobs.variant) {
          acc[e] = value + blobs.variant;
        } else if (value) {
          acc[e] = value;
        }
        return acc;
      }, {});

      if (!res.author && res.Labels && res.Labels.maintainer) {
        res.author = blobs.config.Labels.maintainer;
        delete res.Labels.maintainer;
      }

      if (res.Labels) {
        historyCustomLabels
          .filter((label) => res.Labels[label])
          .forEach((label) => {
            res[`custom-label-${label}`] = res.Labels[label];
            delete res.Labels[label];
          });
      }

      return res;
    };
  </script>
  <style>
    h2 {
      flex-grow: 1;
      display: flex;
      flex-direction: row;
      align-items: center;
    }
    h2 .material-icons {
      margin-left: .25em;
    }
  </style>
</tag-history>
